//-----------------------------------------------------------------------
// <copyright file="SpecifyFormatProvider.cs">
//     Copyright (c) Nicole Calinoiu. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
using System;
using System.Globalization;

using Microsoft.FxCop.Sdk;
using Microsoft.VisualStudio.CodeAnalysis.Extensibility;

using Bordecal.FxCop.Sdk;

namespace Bordecal.FxCop.Rules.Globalization
{
	/// <summary>
	/// Rule that detects method calls that do not supply an <see cref="IFormatProvider"/> that could be passed via another overload.
	/// </summary>
	internal sealed class SpecifyFormatProvider : IntrospectionRule
	{
		#region Constructors

		/// <summary>
		/// Initializes a new instance of the <see cref="SpecifyFormatProvider"/> class.
		/// </summary>
		public SpecifyFormatProvider()
			: base(RuleDescriptorFactory.CreateSingleResolutionDescriptor<SpecifyFormatProvider>(
				RuleCategory.Globalization,
				"Specify format provider",
				"Always use an overload that takes an IFormatProvider or CultureInfo, if one exists.",
				75,
				FixCategories.NonBreaking,
				MessageLevel.Warning,
				"{0} makes a call to {1} that does not explicitly provide a format provider. This should be replaced with a call to {2}."))
		{
		}

		#endregion

		#region Methods

		/// <summary>
		/// Verifies compliance of a type member with the rule.
		/// </summary>
		/// <param name="member">The target member.</param>
		/// <returns>A collection of found problems.</returns>
		public override ProblemCollection Check(Member member)
		{
			Method method = member as Method;
			if (method != null)
			{
				foreach (Instruction instruction in method.Instructions)
				{
					switch (instruction.OpCode)
					{
						case OpCode.Call:
						case OpCode.Callvirt:
						case OpCode.Newobj:
							Method invokedMethod = (Method)instruction.Value;
							if (!SpecifyFormatProvider.ShouldIgnoreMethod(invokedMethod, method))
							{
								Method methodWithExtraParameter = invokedMethod.GetOverloadWithExtraParameters(FrameworkTypes.IFormatProvider);
								if (methodWithExtraParameter != null)
								{
									this.AddProblem(instruction, method.FullName, invokedMethod.FullName, methodWithExtraParameter.FullName);
								}
							}

							break;
					}
				}
			}

			return this.Problems;
		}

		private static bool ShouldIgnoreMethod(Method method, Method containingMethod)
		{
			bool result = false;

			if ((method.DeclaringType == FrameworkTypes.Activator) && method.Name.Name.Equals("CreateInstance", StringComparison.Ordinal))
			{
				result = true;
			}
			else if ((method.DeclaringType == FrameworkTypes.Char) && method.Name.Name.Equals("ToString", StringComparison.Ordinal))
			{
				result = true;
			}
			else if ((method.DeclaringType == FrameworkTypes.ComponentResourceManager) && method.Name.Name.Equals("ApplyResources", StringComparison.Ordinal))
			{
				if (containingMethod.Name.Name.Equals("InitializeComponent", StringComparison.Ordinal) &&
					containingMethod.DeclaringType.IsAssignableTo(FrameworkTypes.Control))
				{
					result = true;
				}
			}

			return result;
		}

		#endregion
	}
}